package stringx

import (
	"prj-layout/pkg/utf8x"
	"unicode/utf8"
	"unsafe"
)

// ToString 把 []byte 转换为 string 没有多余的内存开销。
//
// 使用该方法需要了解到 []byte 将和 string 公用一块内存， 修改 []byte 的数据将导致 string 发生变化，
// 这打破了字符串不可以修改的特性，如果你恰恰需要这么做，可能非常的有用。
// 要保证字符串不可变的特性，就必须保证 []byte 不会发生变化，或者立即消费 string,
// 往往这个非常的有用， 比如我们需要打印日志：
//
// b := []byte("hello word")
// log.Println(ToString(b))
//
// 尽快的消耗掉 string 是个好主意， 也可以遗忘掉 []byte 后面不在使用这个， 而只使用 string。
//
// 比较好的例子是 exstrings.UnsafePad 系列函数，在函数内部使用 []byte 作为字符串缓冲区，返回字符串通过该方法转换。
func ToString(s []byte) string {
	return *(*string)(unsafe.Pointer(&s))
}

// Reverse 反转字符串，通过 https://golang.org/doc/code.html#Library 收集
// 使用 utf8.DecodeRuneInString 改进性能，请见：https://github.com/thinkeridea/go-extend/issues/5
func Reverse(s string) string {
	var start, size, end int
	buf := make([]byte, len(s))
	for end < len(s) {
		_, size = utf8.DecodeRuneInString(s[start:])
		end = start + size
		copy(buf[len(buf)-end:], s[start:end])
		start = end
	}

	return ToString(buf)
}

// ReverseASCII 反转字符串, 只支持单字节编码，可以提供更快地反转
func ReverseASCII(s string) string {
	b := []byte(s)
	for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
		b[i], b[j] = b[j], b[i]
	}
	return ToString(b)
}

// SubString 是 utf8x.RuneSubString 的别名，提供字符数量截取字符串的方法，针对多字节字符安全高效的截取
// 如果 start 是非负数，返回的字符串将从 string 的 start 位置开始，从 0 开始计算。
// 例如，在字符串 “abcdef” 中，在位置 0 的字符是 “a”，位置 2 的字符串是 “c” 等等。
//
// 如果 start 是负数，返回的字符串将从 string 结尾处向前数第 start 个字符开始。
// 如果 string 的长度小于 start，将返回空字符串。
//
// 如果提供了正数的 length，返回的字符串将从 start 处开始最多包括 length 个字符（取决于 string 的长度）。
//
// 如果提供了负数的 length，那么 string 末尾处的 length 个字符将会被省略（若 start 是负数则从字符串尾部算起）。
// 如果 start 不在这段文本中，那么将返回空字符串。
//
// 如果提供了值为 0 的 length，返回的子字符串将从 start 位置开始直到字符串结尾。
func SubString(s string, start, length int) string {
	return utf8x.RuneSubString(s, start, length)
}
